import * as THREE from "three";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
import { TWEEN } from "three/examples/jsm/libs/tween.module.min.js";
import { getDatasByIndex } from "./modelDB.js";
import { FXAAShader } from 'three/examples/jsm/shaders/FXAAShader.js'
import { EffectComposer } from "three/examples/jsm/postprocessing/EffectComposer.js";
import { RenderPass } from "three/examples/jsm/postprocessing/RenderPass.js";
import { ShaderPass } from 'three/examples/jsm/postprocessing/ShaderPass.js'
import { OutlinePass } from "three/examples/jsm/postprocessing/OutlinePass.js";
export default class HomeClass {
  constructor() {
    this.container = document.getElementById("container");
    this.scene = null;
    this.camera = null;
    this.renderer = null;
    this.controls = null;
    this.requestId = null;
    this.composer = null;
    this.outlinePass = null;
    this.clipPlane = null;
    this.modelName = null;
    this.groups = new THREE.Group();
    this.init();
  }
  init() {
    this.initScene();
    this.initLight();
    this.initCamera();
    this.initRender();
    this.initControls();
    window.onresize = () => {
      this.onWindowResize();
    };
    document.oncontextmenu = e => {
      e.preventDefault();
    };
    // this.container.addEventListener("click", e => { this.onClick(e); }, false);
  }
  positionModel(obj) {
    !this.outlinePass && this.initOutlinePass()
    let _name = obj.name.replace(/\s+/g, '_').replace(/\./g, '');
    let _model = this.scene.getObjectByName(_name);
    if (this.clipPlane.constant != obj.position[6]) {
      this.doClipPlane(true);
      new TWEEN.Tween({
        n: this.clipPlane.constant,
      }).to({
        n: obj.position[6],
      }, 1500)
        .easing(TWEEN.Easing.Linear.None)
        .onUpdate(obj => {
          this.clipPlane.constant = obj.n;
        })
        .onComplete(() => {
          this.flyAnimation(obj.position, 800, _model);
        })
        .start();
    } else {
      this.flyAnimation(obj.position, 800, _model);
    }
  }
  flyAnimation(obj, time = 1000, model = null) {
    new TWEEN.Tween({
      x1: this.camera.position.x,
      y1: this.camera.position.y,
      z1: this.camera.position.z,
      x2: this.controls.target.x,
      y2: this.controls.target.y,
      z2: this.controls.target.z
    }).to({
      x1: obj[0],
      y1: obj[1],
      z1: obj[2],
      x2: obj[3],
      y2: obj[4],
      z2: obj[5]
    }, time)
      .easing(TWEEN.Easing.Linear.None)
      .onUpdate(o => {
        this.camera.position.set(o.x1, o.y1, o.z1);
        this.controls.target.set(o.x2, o.y2, o.z2);
        this.controls.update();
      })
      .onStart(() => {
        this.controls.enabled = false;
      })
      .onComplete(() => {
        this.controls.enabled = true;
        model && (this.outlinePass.selectedObjects = [model]);
      }).start();

  }
  initModel(name) {
    let that = this;
    return new Promise((resolve, reject) => {
      getDatasByIndex(`../../static/models/${name}.glb`, 2).then(res => {
        const progressElement = document.getElementById("progress");
        if (res) {
          const mUrl = URL.createObjectURL(new Blob([res.value]));
          const manager = new THREE.LoadingManager();
          manager.onLoad = () => {
            this.modelName = name;
            progressElement.style.display = 'none';
            that.sceneInit();
            that.animate();
            resolve(true);
          };
          const loader = new GLTFLoader(manager);
          loader.load(mUrl, (gltf) => {
            // console.log(gltf)
            that.scene.add(gltf.scene);
            URL.revokeObjectURL(mUrl);
          }, (xhr) => {
            let _loadPercent = parseInt((xhr.loaded * 100) / xhr.total);
            if (_loadPercent > 90) {
              progressElement.innerText = `加速渲染中...`;
            } else {
              progressElement.innerText = `努力加载中... ${_loadPercent}%`;
            }
          });
        } else {
          progressElement.innerText = `模型加载失败，请刷新 ！`;
          resolve(false);
        }
      });
    })
  }
  doClipPlane(flag = false) {
    let _constant = 0;
    switch (this.modelName) {
      case 'home':
        _constant = 25;
        if (flag) {
          this.camera.position.set(31.566, 45.033, -3.238);
          this.controls.target.set(-5.031, 13.99, -3.238);
          this.controls.update();
        }
        break;
      case 'zh':
        _constant = 18;
        if (flag) {
          this.camera.position.set(26.588, 29.144, -2.086);
          this.controls.target.set(-2, 6, -2);
          this.controls.update();
        }
        break;
      case 'kjc':
        _constant = 18;
        if (flag) {
          this.camera.position.set(19.026, 30.354, 1.239);
          this.controls.target.set(-0.001, 9.0249, 1.202);
          this.controls.update();
        }
        break;
      case 'gl':
        _constant = 18;
        if (flag) {
          this.camera.position.set(21.921, 29.561, -0.164);
          this.controls.target.set(-3, 5, 0);
          this.controls.update();
        }
        break;
      case 'c1':
        _constant = 18;
        if (flag) {
          this.camera.position.set(16.819, 28.458, -1.523);
          this.controls.target.set(-3.025, 5.021, -1.530);
          this.controls.update();
        }
        break;
      default:
        break;
    }
    if (!flag) {
      this.outlinePass && (this.outlinePass.selectedObjects = []);
      if (this.clipPlane) {
        this.clipPlane.constant = _constant;
      } else {
        this.clipPlane = new THREE.Plane(new THREE.Vector3(0, -1, 0), _constant);
        this.renderer.localClippingEnabled = true;
        this.renderer.clippingPlanes = [this.clipPlane];
      }
      this.sceneInit();
    }
  }
  sceneInit() {
    switch (this.modelName) {
      case 'home':
        this.flyAnimation([51, 28, -5, -2, 10, -5], 800);
        break;
      case 'zh':
        this.flyAnimation([32, 20, -3, -2, 6, -2], 800);
        break;
      case 'kjc':
        this.flyAnimation([29, 19, 1.5, 1, 8, 2], 800);
        break;
      case 'gl':
        this.flyAnimation([28, 17, 0, -3, 5, 0], 800);
        break;
      case 'c1':
        this.flyAnimation([27, 17, -1, -3, 5, -2], 800);
        break;
      default:
        break;
    }
  }
  animate() {
    // console.log(this.camera.position)
    // console.log(this.controls.target)
    // console.log('--------')
    TWEEN.update();
    if (this.composer) {
      this.composer.render();
    } else {
      this.renderer.render(this.scene, this.camera);
    }
    this.requestId = requestAnimationFrame(() => {
      this.animate();
    });
  }
  initOutlinePass() {
    this.composer = new EffectComposer(this.renderer);
    let renderPass = new RenderPass(this.scene, this.camera);
    this.composer.addPass(renderPass);
    var effectFXAA = new ShaderPass(FXAAShader);
    effectFXAA.uniforms.resolution.value.set(1 / this.container.clientWidth, 1 / this.container.clientHeight);
    effectFXAA.renderToScreen = true;
    this.composer.addPass(effectFXAA);
    this.outlinePass = new OutlinePass(
      new THREE.Vector2(
        this.container.clientWidth,
        this.container.clientHeight
      ),
      this.scene,
      this.camera
    );
    this.outlinePass.edgeStrength = 10.0;
    this.outlinePass.edgeGlow = 1.0;
    this.outlinePass.usePatternTexture = false;
    this.outlinePass.edgeThickness = 4.0;
    this.outlinePass.downSampleRatio = 1.0;
    this.outlinePass.pulsePeriod = 3;
    this.outlinePass.visibleEdgeColor.set("#3FDAFD");
    this.outlinePass.hiddenEdgeColor = new THREE.Color(0, 0, 0);
    this.outlinePass.clear = true;
    this.composer.addPass(this.outlinePass);
  }
  changeCameras(obj, flag = true) {
    let cameraGroup = this.groups.children.find(item => item.name == 'camera-groups');
    if (obj.id === 0) {
      cameraGroup.children.forEach((item) => {
        item.visible = true;
      })
      this.sceneInit(obj.type);
    } else {
      cameraGroup.children.forEach((item) => {
        if (flag && item.name == 'camera-' + obj.id) {
          item.visible = true;
        } else {
          item.visible = false;
        }
      })
      if (flag) {
        this.flyAnimation([obj.position[0] + 8, obj.position[1] + 4, obj.position[2], obj.position[0], obj.position[1], obj.position[2]], 800);
      } else {
        this.scene.traverse((child) => {
          if (child.isMesh) {
            !child.material.transparent && (child.material.transparent = true);
            child.material.opacity = 1;
          }
        })
        this.sceneInit();
      }
    }
  }
  makeCameras(arrs) {
    let cameraGroup = this.groups.children.find(item => item.name == 'camera-groups');
    this.scene.traverse((child) => {
      if (child.isMesh) {
        !child.material.transparent && (child.material.transparent = true);
        child.material.opacity = 0.1;
      }
    })
    if (!cameraGroup) {
      let group = new THREE.Group();
      group.name = 'camera-groups';
      this.groups.add(group);
      let texture = new THREE.TextureLoader().load("../../static/images/camera.png");
      let sprite = new THREE.Sprite(new THREE.SpriteMaterial({
        map: texture,
        depthTest: false,
      }));
      sprite.material.map.encoding = THREE.sRGBEncoding;
      arrs.forEach((item, i) => {
        if (i > 0) {
          let camera = sprite.clone();
          camera.name = 'camera-' + item.id;
          camera.position.set(item.position[0], item.position[1], item.position[2]);
          group.add(camera);
        }
      })
    } else {
      cameraGroup.children.forEach((item) => {
        item.visible = true;
      })
    }
  }
  initScene() {
    this.scene = new THREE.Scene();
    this.groups.name = 'help-group';
    this.scene.add(this.groups);
    // this.scene.add(new THREE.AxesHelper(50));
  }
  initLight() {
    let group = new THREE.Group();
    group.name = "light-group";
    const light = new THREE.DirectionalLight(0xffffff, 2.0);
    light.position.set(1, 1, 1);
    group.add(light);
    const light1 = new THREE.DirectionalLight(0xffffff, 2.0);
    light1.position.set(-1, 1, -1);
    group.add(light1);
    const light2 = new THREE.DirectionalLight(0xffffff, 1.0);
    light2.position.set(0, -1, 0);
    group.add(light2);
    this.scene.add(group);
  }
  initCamera() {
    this.camera = new THREE.PerspectiveCamera(
      55,
      this.container.clientWidth / this.container.clientHeight,
      0.1,
      1000
    );
  }
  initRender() {
    this.renderer = new THREE.WebGLRenderer({
      antialias: true,
      // logarithmicDepthBuffer: true,
      alpha: true,
    });
    this.renderer.setPixelRatio(window.devicePixelRatio);
    this.renderer.setSize(
      this.container.clientWidth,
      this.container.clientHeight
    );
    this.renderer.outputEncoding = THREE.sRGBEncoding;
    // this.renderer.toneMappingExposure = 1.0;
    // this.renderer.toneMapping = THREE.ACESFilmicToneMapping;
    this.container.appendChild(this.renderer.domElement);
  }
  initControls() {
    this.controls = new OrbitControls(this.camera, this.renderer.domElement);
    // this.controls.minPolarAngle = 0.2;
    // this.controls.maxPolarAngle = 1.4;
    this.controls.minDistance = 1;
    this.controls.maxDistance = 800;
  }
  onWindowResize() {
    this.camera.aspect =
      this.container.clientWidth / this.container.clientHeight;
    this.camera.updateProjectionMatrix();
    this.renderer.setSize(
      this.container.clientWidth,
      this.container.clientHeight
    );
  }
  disposeChild(mesh) {
    if (mesh instanceof THREE.Mesh) {
      if (mesh.geometry) {
        mesh.geometry.dispose();
      }
      if (mesh.material.texture) {
        mesh.material.texture.dispose();
      }
      if (mesh.material) {
        mesh.material.dispose();
      }
    }
    if (mesh instanceof THREE.Group) {
      mesh.clear();
    }
    if (mesh instanceof THREE.Object3D) {
      mesh.clear();
    }
  }
  destroy() {
    if (this.requestId) {
      cancelAnimationFrame(this.requestId);
      this.requestId = null;
    }
    this.scene.traverse(item => {
      this.disposeChild(item);
    });
    this.scene.clear();
    this.scene = null;
    this.camera = null;
    if (this.outlinePass) {
      this.outlinePass.dispose();
      this.outlinePass = null;
    }
    this.composer && (this.composer = null);
    TWEEN.removeAll();
    if (this.renderer) {
      this.renderer.dispose();
      this.renderer.forceContextLoss();
      this.renderer.domElement = null;
      this.renderer.content = null;
      this.renderer = null;
    }
    window.onresize = null;
    window.oncontextmenu = null;
  }
}
